﻿/*
	flad : Copyright 2010 Dale J Williams

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/
package lads.flad.tween {
	
	import lads.flad.tween.easing.Quad;
	
	/**
	 * Tween: A numerical animator.
	 * @author Dale J Williams
	 * @version 2.0
	 */
	public class Tween {
		
		private var _manager			:TweenManager;
		private var _target				:Object;
		
		private var _init				:Object;
		private var _dest				:Object;
		
		private var _startTime			:Number;
		private var _delay				:Number;
		private var _time				:Number;
		private var _duration			:Number;
		private var _pc					:Number;
		private var _key				:String;
		
		private var _ease				:Function;
		private var _onStart			:Function;
		private var _onStartParams		:Array;
		private var _onUpdate			:Function;
		private var _onUpdateSet		:Boolean;
		private var _onUpdateParams		:Array;
		private var _onComplete			:Function;
		private var _onCompleteParams 	:Array;
		
		private var _completed			:Boolean;
		
		private var _autoVisible		:Boolean;
		private var _endVisible			:Object;
		private var _autoRemove			:Boolean;
		private var _tracing			:Boolean;
		
		public function Tween($target:Object=null, $duration:Number=0, $properties:Object=null) {
			if ($target && $properties)
				init($target, $duration, $properties);
		}
		
		public function init($target:Object, $duration:Number, $properties:Object):void {
			_target = $target;
			_duration = $duration;
			_delay = 0;
			_init = { }; 														// initial properties
			_dest = { }; 														// destination properties
			for (_key in $properties) {
				switch(_key) {
					case "ease":												// assign tween internal properties			
					case "onStart":
					case "onUpdate":
					case "onStartParams":
					case "onUpdateParams":
					case "onComplete":
					case "onCompleteParams":
					case "delay": 												
						this["_" + _key] = $properties[_key]; 					
						break;
					case "autoAlpha":											// handle automatic visibility
					case "autoVisible":
						_init.alpha = _target.alpha;
						_dest.alpha = $properties.autoAlpha;
						_autoVisible = true;									
						break;
					case "autoRemove":
						_autoRemove = $properties.autoRemove;
						break;
					case "scale":												// automatic scaling
						_init.scaleX = _target.scaleX;
						_init.scaleY = _target.scaleY;
						_dest.scaleX = _dest.scaleY = $properties.scale;
						break;
					case "visible":
						_target.visible = true;
						_endVisible = $properties.visible;
						break;
					default: 													// transfer all other poperties to destination
						_init[_key] = _target[_key];
						_dest[_key] = $properties[_key];
				}
			}
			_duration *= 1000;
			_delay *= 1000;			
			_onUpdateSet = _onUpdate != null;
			_startTime = _manager.time + _delay;
			if (_ease == null) _ease = Quad.easeOut; 							// set automatic easing
			if (_autoVisible) _target.visible = true;
			if (_onStart != null) _onStart.apply(_target, _onStartParams); 		// call complete if needed;
			if (_duration <= 0) complete();
		}
		
		public function update($t:int):void {
			_time = $t - _startTime;											// calculate time
			if (_time < 0) return; 												// if time is less than zero, bail
			_pc = _ease(_time, 0, 1, _duration); 								// calculate progress
			for (_key in _dest) 												
				_target[_key] = _dest[_key]*_pc + _init[_key]*(1-_pc);			// applies values
			if (_onUpdateSet) 
				_onUpdate.apply(_target, _onUpdateParams);						// calls onUpdate if required
			if (_time >= _duration) complete();									// pushes complete when ready
		}
		
		public function complete():void {
			if (_autoVisible) _target.visible = _target.alpha > 0;				// toggles visibility if needed	
			if (_autoRemove && _target.parent) 									// removes object from stage if requested
				_target.parent.removeChild(_target);
			if (_endVisible is Boolean) _target.visible = _endVisible;			// automatically make visible or invisible
			for (_key in _dest) 
				_target[_key] = _dest[_key];									// corrects end position
			_completed = true;													// declares complete
			if (_onComplete != null)								
				_onComplete.apply(_target, _onCompleteParams);					// calls onComplete if required
			_manager.kill(_target); 											// removes tween
		}
		
		public function toString():String {
			var str:String = "Tween: ";
			for (_key in _dest) 												
				str += "\n\t" + _key + ":" + _init[_key] + " > " + _target[_key] + " > " + _dest[_key];
			return str;
		}
		
		/**
		 * The sinner is the target of the tweening.
		 */
		public function get target():Object { return _target; }
		
		/**
		 * The function called when the TDTween is over.
		 */
		public function get onComplete():Function { return _onComplete; }		
		public function set onComplete(value:Function):void {
			_onComplete = value;
		}
		
		/**
		 * The function called on each frame that the tweening occurs.
		 */
		public function get onUpdate():Function { return _onUpdate; }		
		public function set onUpdate(value:Function):void {
			_onUpdate = value;
			_onUpdateSet = _onUpdate == null;
		}
		
		/**
		 * The function called when the tweening commences.
		 */
		public function get onStart():Function { return _onStart; }		
		public function set onStart(value:Function):void {
			_onStart = value;
		}
		
		/**
		 * The ease controls the rate of tweening.
		 */
		public function get ease():Function { return _ease; }		
		public function set ease(value:Function):void {
			_ease = value;
		}
		
		/**
		 * Represents the progress of the tween's manipulation.
		 */
		public function get percent():Number { return _pc; }
		
		/**
		 * Returns true if tween has completed.
		 */
		public function get completed():Boolean { return _completed; }
		
		public function get manager():TweenManager { return _manager; }		
		public function set manager(value:TweenManager):void {
			_manager = value;
		}
	}
	
}